<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" >

<title>Redux 源码解析-createStore | Zero2key&#39;s Blog</title>
<meta name="description" content="">

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">

<link rel="shortcut icon" href="https://zero2key.gitee.io/favicon.ico">
<link rel="stylesheet" href="https://zero2key.gitee.io/styles/main.css">


  
    <link rel="stylesheet" href="https://unpkg.com/gitalk/dist/gitalk.css" />
  

  


<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.bootcss.com/highlight.js/9.12.0/highlight.min.js"></script>

<link rel="stylesheet" href="https://unpkg.com/aos@next/dist/aos.css" />


  </head>
  <body>
    <div id="app" class="main">

      <div class="sidebar" :class="{ 'full-height': menuVisible }">
  <div class="top-container" data-aos="fade-right">
    <div class="top-header-container">
      <a class="site-title-container" href="https://zero2key.gitee.io">
        <img src="https://zero2key.gitee.io/images/avatar.png" class="site-logo">
        <h1 class="site-title">Zero2key&#39;s Blog</h1>
      </a>
      <div class="menu-btn" @click="menuVisible = !menuVisible">
        <div class="line"></div>
      </div>
    </div>
    <div>
      
        
          <a href="/" class="site-nav">
            首页
          </a>
        
      
        
          <a href="/archives" class="site-nav">
            归档
          </a>
        
      
        
          <a href="/tags" class="site-nav">
            标签
          </a>
        
      
    </div>
  </div>
  <div class="bottom-container" data-aos="flip-up" data-aos-offset="0">
    <div class="site-description">
      
    </div>
    <div class="site-footer">
      Powered by <a href="https://github.com/getgridea/gridea" target="_blank" style="color:#fff;">Gridea</a> And <a style="display: inline-block;
    width: 80px;line-height: 12px;font-size: 12px;position: relative;" href="https://www.upyun.com/?utm_source=lianmeng&utm_medium=referral" target="_blank">&nbsp;<img style="height: 40px;position: absolute;left:0;top:-15px;" src=""></img></a>
    </div>
  </div>
</div>


      <div class="main-container">
        <div class="content-container" data-aos="fade-up">
          <div class="post-detail">
            <h2 class="post-title">Redux 源码解析-createStore</h2>
            <div class="post-date">2019-09-08</div>
            
            <div class="post-content">
              <h2 id="源码">源码</h2>
<pre><code class="language-js">export default function createStore(reducer, preloadedState, enhancer) {
  // preloadedState 和 enhancer 不可以同时为 function
  if (
    (typeof preloadedState === 'function' &amp;&amp; typeof enhancer === 'function') ||
    (typeof enhancer === 'function' &amp;&amp; typeof arguments[3] === 'function')
  ) {
    throw new Error(
      'It looks like you are passing several store enhancers to ' +
        'createStore(). This is not supported. Instead, compose them ' +
        'together to a single function.'
    )
  }
  
  // 如果 preloadedState 为 function，传入的 preloadedState 为中间件
  if (typeof preloadedState === 'function' &amp;&amp; typeof enhancer === 'undefined') {
    // 将 preloadedState 赋值给 enhancer
    enhancer = preloadedState
    // 预定义的 state 为 undefined
    preloadedState = undefined
  }

  // 如果 enhancer 为 function
  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
    // 返回 中间件包装的 createStore
    return enhancer(createStore)(reducer, preloadedState)
  }

  // reducer 必须为 function
  if (typeof reducer !== 'function') {
    throw new Error('Expected the reducer to be a function.')
  }
  
  // 初始化 currentReducer
  let currentReducer = reducer
  // 初始化当前 state 为 preloadedState
  let currentState = preloadedState
  // 当前监听器列表
  let currentListeners = []
  // 下一次监听器列表
  let nextListeners = currentListeners
  // 派发标记位
  let isDispatching = false

  // 确保下一次监听器列表和当前监听器列表不是同一个数组
  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      nextListeners = currentListeners.slice()
    }
  }

  function getState() {
    // 如果正在派发中抛出异常
    if (isDispatching) {
      throw new Error(
        'You may not call store.getState() while the reducer is executing. ' +
          'The reducer has already received the state as an argument. ' +
          'Pass it down from the top reducer instead of reading it from the store.'
      )
    }

    // 返回当前 state
    return currentState
  }

  // 订阅函数
  function subscribe(listener) {
    // 监听器必须为 function
    if (typeof listener !== 'function') {
      throw new Error('Expected the listener to be a function.')
    }

    // 如果正在派发中抛出异常
    if (isDispatching) {
      throw new Error(
        'You may not call store.subscribe() while the reducer is executing. ' +
          'If you would like to be notified after the store has been updated, subscribe from a ' +
          'component and invoke store.getState() in the callback to access the latest state. ' +
          'See https://redux.js.org/api-reference/store#subscribelistener for more details.'
      )
    }

    // 订阅标记
    let isSubscribed = true

    ensureCanMutateNextListeners()
    // 将监听器加入下一次监听器列表
    nextListeners.push(listener)

    // 取消订阅
    return function unsubscribe() {
      // 如果订阅标记为 false，表示为已经取消订阅，直接返回
      if (!isSubscribed) {
        return
      }

      // 如果正在派发中抛出异常
      if (isDispatching) {
        throw new Error(
          'You may not unsubscribe from a store listener while the reducer is executing. ' +
            'See https://redux.js.org/api-reference/store#subscribelistener for more details.'
        )
      }

      // 设置订阅标记为 false
      isSubscribed = false

      ensureCanMutateNextListeners()

      // 从下一次订阅列表中删除当前取消订阅的监听器
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
      // 设置当前订阅列表为 null
      currentListeners = null
    }
  }

  // 派发函数
  function dispatch(action) {
    // action 必须为纯对象
    if (!isPlainObject(action)) {
      throw new Error(
        'Actions must be plain objects. ' +
          'Use custom middleware for async actions.'
      )
    }

    // action 必须包含 type
    if (typeof action.type === 'undefined') {
      throw new Error(
        'Actions may not have an undefined &quot;type&quot; property. ' +
          'Have you misspelled a constant?'
      )
    }

    // 如果正在派发中抛出异常
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }

    try {
      // 设置派发标记位为 true
      isDispatching = true
      // 通过 reducer 依据当前 state 和 action 获取新的 state
      currentState = currentReducer(currentState, action)
    } finally {
      // 设置派发标记位为 false
      isDispatching = false
    }

    // 触发所有订阅的监听器
    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i &lt; listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }

    // 返回 action
    return action
  }

  // 替换 reducer
  function replaceReducer(nextReducer) {
    // reducer 必须为 function
    if (typeof nextReducer !== 'function') {
      throw new Error('Expected the nextReducer to be a function.')
    }

    currentReducer = nextReducer

    // 派发 REPLACE action
    dispatch({ type: ActionTypes.REPLACE })
  }

  function observable() {
    const outerSubscribe = subscribe
    return {
      subscribe(observer) {
        if (typeof observer !== 'object' || observer === null) {
          throw new TypeError('Expected the observer to be an object.')
        }

        function observeState() {
          if (observer.next) {
            observer.next(getState())
          }
        }

        observeState()
        const unsubscribe = outerSubscribe(observeState)
        return { unsubscribe }
      },

      [$$observable]() {
        return this
      }
    }
  }

  /**
   * 派发 INIT action
   * 依据当前 state，经过 reducer 获取默认 state
   */
  dispatch({ type: ActionTypes.INIT })

  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  }
}
</code></pre>

            </div>
            
              <div class="tag-container">
                
                  <a href="https://zero2key.gitee.io/tag/redux/" class="tag">
                    Redux
                  </a>
                
                  <a href="https://zero2key.gitee.io/tag/yuan-ma-jie-xi/" class="tag">
                    源码解析
                  </a>
                
              </div>
            
            
              <div class="next-post">
                <div class="next">下一篇</div>
                <a href="https://zero2key.gitee.io/post/KbkMQoUmf/">
                  <h3 class="post-title">
                    Redux 源码解析-简介
                  </h3>
                </a>
              </div>
            

            
              
                <div id="gitalk-container" data-aos="fade-in"></div>
              

              
            

          </div>

        </div>
      </div>
    </div>

    <script src="https://unpkg.com/aos@next/dist/aos.js"></script>

<script type="application/javascript">

AOS.init();

hljs.initHighlightingOnLoad()

var app = new Vue({
  el: '#app',
  data: {
    menuVisible: false,
  },
})

</script>


  
  
    <script src="https://unpkg.com/gitalk/dist/gitalk.min.js"></script>
    <script>

      var gitalk = new Gitalk({
        clientID: '077888762ae810c37005',
        clientSecret: 'c443e600f8e2687909ccd669db5353cd0839979a',
        repo: 'Zero2key',
        owner: 'Zero2key',
        admin: ['Zero2key'],
        id: location.pathname,      // Ensure uniqueness and length less than 50
        distractionFreeMode: false  // Facebook-like distraction free mode
      })

      gitalk.render('gitalk-container')

    </script>
  

  




  </body>
</html>
